<?php
namespace App\User\Services;

use App\Common\Library\DES;
use App\Exceptions\ApiException;
use App\Services\CommonBaseService;
use App\Sys\Facades\SysLogFacade;
use App\Sys\Facades\SysMenuFuncAuthFacade;
use App\Sys\Models\SysAppModel;
use App\User\Facades\UserBindAppFacade;
use App\User\Models\UserInfoModel;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;

class UserInfoService extends CommonBaseService
{
    protected $cache = true;

    protected $cacheBucket = 'SysAdminUser:';

    protected $tokenBucket = 'AdminToken:';

    protected $activeBucket = "Active:";

    protected UserBindAppService $userBindAppService;

    protected SysAppModel $sysAppModel;

    protected WechatService $wechatService;

    public function __construct(UserInfoModel $model)
    {
        $this->model = $model;
        $this->userBindAppService = app()->make(UserBindAppService::class);
        $this->sysAppModel = app()->make(SysAppModel::class);
        $this->wechatService = app()->make(WechatService::class);
    }

    /**
     * 设置token缓存时间
     * @param $token
     * @param $user
     */
    public function setToken($token, $user)
    {
        Cache::put($this->getTokenKey($token), $user, config('cache.token'));
    }

    /**
     * 移除token缓存
     * */
    public function forgotToken($token)
    {
        return Cache::pull($this->getTokenKey($token));
    }

    /**
     * 获取token缓存key
     * @param $token
     * @return string
     */
    private function getTokenKey($token)
    {
        return $this->tokenBucket . $token;
    }

    /**
     * @param $params
     * @return string
     */
    public function loginByPassword($params)
    {
        if (empty($params)) {
            return;
        }
        $userInfo = $this->model->getUserByName($params['user_name']);
        if (empty($userInfo)) {
            throw new ApiException(10006, '用户不存在');
        }
        $prefixed = $userInfo['salt'];
        $password = $prefixed . $params['password'];
        $loginPasswordMd5 = md5($password);
        if ($loginPasswordMd5 != $userInfo['password']) {
            throw new ApiException(10001, '密码错误');
        }
        $clientType = $params['client_type'] ?? '';
        return $this->initLoginState($userInfo, $clientType, $params['machine_info'] ?? '');
    }

    /**
     * 获取后台用户列表
     * */
    public function getAdminUserList($params)
    {
        $pageSize = empty($params['page_size']) ? 10 : $params['page_size'];
        $page = empty($params['page']) ? 1 : $params['page'];
        $skip = ($page - 1) * $pageSize; //页面记录的开始位置，即偏移量
        $where = [];
        $where[] = ['a.status', '<', '2'];
        $query = $this->model->alias('a')
            ->leftJoin('sys_role as b', 'a.role_id', '=', 'b.id')
            ->where($where);
        if (!empty($params['keyword'])) {
            $keyword = $params['keyword'];
            $query->where(function ($queryStr) use ($keyword) {
                $queryStr->where('a.user_name', 'like', "%" . $keyword . "%")
                    ->orWhere('a.real_name', 'like', "%" . $keyword . "%");
            });
        }
        $totalCount = $query->count();
        $list = $query->skip($skip)
            ->limit($pageSize)
            ->selectRaw('a.id,a.user_name,a.real_name,a.phone,a.last_login_time,a.status,a.create_time,a.role_id,b.role_name')
            ->get()->toArray();
        $results = buildPage($list, $skip, $page, $pageSize, $totalCount);
        return $results;
    }

    /**
     * 新增后台修改用户信息
     * @return array
     * */
    public function saveAdminUser($params)
    {
        $userData = $this->buildAddAdminUser($params);
        if (!empty($userData['id'])) {
            $id = $userData['id'];
            $this->updateBy(['id' => $userData['id']], $userData);
            //求改当前用户的信息需要更新redis缓存
            if ($id == $this->getAuthUserId()) {
                $this->updateUserInfoCache();
            }
        } else {
            $id = $this->model->insertGetId($userData);
        }
        return $id;
    }

    /**
     * 小程序登录
     * @param $params
     * @return int
     */
    public function wechatOauth($params)
    {
        $code = $params['code'];
        $encryptedData = $params['encrypted_data'];
        $iv = $params['iv'];
        $session = $this->wechatService->getSessionInfo($code);
        if (!empty($session['openid'])) {
            $sessionKey = $session['session_key'];
            $decryptData = $this->wechatService->aesDecrypt($encryptedData, $sessionKey, $iv);
            return $this->saveWechatUser($session['openid'], [
                'session_key' => $session['session_key'],
                'decrypt_data' => $decryptData
            ]);
        }
        return 0;
    }

    /**
     * 微信注册接口
     * @param $openId
     * @param $authInfo
     * @return int|mixed|null
     */
    public function saveWechatUser($openId, $authInfo)
    {
        $userData = [];
        $userInfo = $this->model->getUserByOpenId($openId);
        $time = nowTime();
        $userId = null;
        if (!empty($authInfo['decrypt_data'])) {
            $userData['avatar'] = $authInfo['decrypt_data']['avatarUrl'];
            $userData['nick_name'] = $authInfo['decrypt_data']['nickName'];
            if(!empty($authInfo['decrypt_data']['watermark']['phoneNumber'])) {
                $userData['phone'] = $authInfo['decrypt_data']['watermark']['phoneNumber'];
            }
            $userData['auth_info'] = json_encode($authInfo, JSON_UNESCAPED_UNICODE);
        }
        if (!empty($userInfo)) {
            $this->model->saveUserAuthInfo($openId, $authInfo);
        } else {
            $userData['create_time'] = $time;
            $userData['role_id'] = 0;
            DB::transaction(function () use ($userData, &$userId) {
                $userId = $this->model->saveUser($userData);
                $app = $this->sysAppModel->getAppIdByCode(SysAppModel::APP_CODE_MINIPROG_FRONT);
                if (!empty($app['id'])) {
                    $this->userBindAppService->saveUserBindApp($userId, $app['id']);
                }
            });
            $userInfo['id'] = $userId;
        }
        if (!empty($userInfo['id'])) {
            return $this->initLoginState($userInfo, 'wechat');
        }
        return '';
    }

    /**
     * 构造编辑用户详情数据
     * @return array
     * */
    private function buildAddAdminUser($params)
    {
        $userData = [];
        if (isset($params['password'])) {
            $userData['salt'] = getRandomStr('PW');
            $password = $userData['salt'] . $params['password'];
            $userData['password'] = md5($password);
        }
        $time = nowTime();
        $userData['update_time'] = $time;
        if (!empty($params['id'])) {
            if (isset($params['password'])) {
                //修改密码要校验
                $this->checkLoginPassword($params['login_password']);
            }
            $userData['id'] = $params['id'];
            if (isset($params['real_name'])) {
                $userData['real_name'] = $params['real_name'];
            }
            if (isset($params['phone'])) {
                $userData['phone'] = $params['phone'];
                $info = $this->model
                    ->where('status', '<', 2)
                    ->where('id', '<>', $params['id'])
                    ->where('phone', $userData['phone'])->first();
                if (!empty($info)) {
                    throw new ApiException(10011, ['phone' => $userData['phone']]);
                }
            }
            if (isset($params['status'])) {
                $userData['status'] = $params['status'];
            }
            if (isset($params['role_id'])) {
                $userData['role_id'] = $params['role_id'];
            }
        } else {
            $userData['user_name'] = $params['user_name'] ?? '';
            $userData['real_name'] = $params['real_name'] ?? '';
            $userData['phone'] = $params['phone'] ?? '';
            $info = $this->model->where('status', '<', 2)
                ->where(function ($queryStr) use ($userData) {
                    $queryStr->where('user_name', '=', $userData['user_name'])
                        ->orWhere('phone', '=', $userData['phone']);
                })->first();
            if (!empty($info)) {
                $info = $info->toArray();
                if ($info['phone'] == $userData['phone']) {
                    throw new ApiException(10011, ['phone' => $userData['phone']]);
                } else {
                    throw new ApiException(10010, ['user_name' => $userData['user_name']]);
                }
            }
            $userData['create_time'] = $time;
            if (isset($params['status'])) {
                $userData['status'] = $params['status'];
            } else {
                $userData['status'] = UserInfoModel::STATUS_DISABLED;
            }
            if (isset($params['role_id'])) {
                $userData['role_id'] = $params['role_id'];
            }
        }
        return $userData;
    }

    /**
     * 根据id获取用户详情
     * */
    public function getUserInfoById($id)
    {
        $info = $this->model->where(['id' => $id])->first();
        $dataInfo = [];
        if (!empty($info)) {
            $dataInfo = $info->toArray();
        }
        return $dataInfo;
    }

    /**
     * 校验登陆密码
     * */
    private function checkLoginPassword($loginPassword)
    {
        $userInfo = $this->getAuthUser();
        if (!empty($userInfo)) {
            $prefixed = $userInfo['salt'];
            $password = $prefixed . $loginPassword;
            $loginPasswordMd5 = md5($password);
            if ($loginPasswordMd5 === $userInfo['password']) {
                return true;
            }
        }
        throw new ApiException(10001);
    }

    /**
     * 更新用户缓存
     * */
    public function updateUserInfoCache($token = '', $user = [])
    {
        if (empty($user)) {
            $userId = $this->getAuthUserId();
            $newUser = $this->getUserInfoById($userId);
        } else {
            $newUser = $user;
        }
        if (empty($token)) {
            if (config('app.login_singleton')) {
                $key = $this->cacheBucket . $this->tokenBucket . $userId . 'admin_token';
                $token = Cache::get($key);
            } else {
                $token = $this->getAuthToken();
            }
        }
        $newUser = $this->getUser($newUser, $token);
        $this->setToken($token, $newUser);
        return $newUser;
    }

    private function getPermission($roleId)
    {
        $data = SysMenuFuncAuthFacade::adminRoleAuth($roleId);
        $permission = [];
        foreach ($data as $value) {
            if (!empty($value['route_path'])) {
                $permission[] = $value['route_path'];
            }
        }
        return $permission;
    }

    /**
     * 用户登出
     * */
    public function logout($token)
    {
        if (empty($token)) {
            return;
        }
        $this->forgotToken($token);
        return true;
    }

    /**
     * 根据token获取用户
     * @param $token
     * @return UserInfoModel | mixed
     */
    public function findOneByToken($token)
    {
        return Cache::get($this->getTokenKey($token));
    }

    /**
     * 获取用户的详细信息
     * @param int $type 0 简略详细 1详细信息
     * */
    public function getUserDetail($type = 0)
    {
        $ret = ['code' => 0, 'data' => []];
        $baseFields = ['id', 'user_name', 'real_name', 'is_super', 'permission', 'nick_name', 'avatar', 'phone'];
        $detailFields = ['id', 'user_name', 'real_name', 'phone', 'is_super', 'permission', 'role_id', 'nick_name', 'avatar'];
        $user = $this->getAuthUser();
        $resultUser = [];
        if ($type) {
            $resultFields = $detailFields;
        } else {
            $resultFields = $baseFields;
        }
        foreach ($resultFields as $value) {
            $resultUser[$value] = $user[$value];
        }
        if(!empty($resultUser['phone'])) {
            //手机号隐藏
            $resultUser['phone'] = $this->maskPhoneNumber($resultUser['phone']);
        }
        $ret['data'] = $resultUser;
        return $ret['data'];
    }

    private function maskPhoneNumber($phoneNumber) {
        // 检查手机号长度是否为11位
        if (strlen($phoneNumber) !== 11) {
            return "1***********";
        }
        // 提取前三位和后四位，其余部分用星号替换
        $maskedNumber = substr($phoneNumber, 0, 3) . str_repeat('*', 5) . substr($phoneNumber, -4);
        return $maskedNumber;
    }

    /**
     * 设置当前用户token
     * @param $userId
     * @param $token
     */
    public function setCacheToken($userId, $token)
    {
        $key = $this->cacheBucket . $this->tokenBucket . $userId . 'admin_token';
        Cache::put($key, $token, config('cache.token'));
    }

    /**
     * 是否最近登录的一个账号
     * @param $userId
     * @param $token
     * @return bool
     */
    public function isLastToken($userId, $token)
    {
        $key = $this->cacheBucket . $this->tokenBucket . $userId . 'admin_token';
        $insideToken = Cache::get($key);
        //如果缓存中不存在token
        if (!$insideToken) {
            $this->setCacheToken($userId, $token);
            return true;
        }
        //如果缓存中的token跟用户传入的token一致
        if ($insideToken == $token) {
            return true;
        }
        return false;
    }

    /**
     * @param array $userInfo
     * @return string
     */
    public function initLoginState(array $userInfo, $clientType, $machineInfo = ''): string
    {
        $now = nowTime();
        $userInfo['last_login_time'] = $now;
        $randomStr = getRandomStr('ADMIN_TOKEN');
        $token = md5($userInfo['user_name'] . $randomStr);
        $this->model->updateData(['last_login_time' => $now], ['id' => $userInfo['id']]);
        $userInfo['client_type'] = $clientType;
        $userInfo = $this->getUser($userInfo, $token);
        $this->setToken($token, $userInfo);
        SysLogFacade::saveSysLoginLog($userInfo['id'], $machineInfo);
        return $token;
    }

    /**
     * @param $newUser
     * @param $token
     * @return mixed
     */
    public function getUser($newUser, $token)
    {
        $clientType = $newUser['client_type'] ?? '';
        $newUser['bind_app'] = UserBindAppFacade::getUserBindApp($newUser['id']);
        if (config('app.login_singleton')) {
            $this->setCacheToken($newUser['id'], $token);
        }
        $newUser['info_completed'] = 0;
        $newUser['client_type'] = $clientType;
        $CacheTokenTimeMinute = config('cache.token');
        $nowTime = time();
        $expiration_time = $nowTime + 60 * $CacheTokenTimeMinute;
        $newUser['expiration_time'] = $expiration_time;//过期时间
        $newUser['token'] = $token;
        $newUser['permission'] = $this->getPermission($newUser['role_id']);
        return $newUser;
    }
}
